package cluster

import (
	"errors"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"math/rand"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"
	"xc/app/helper"
	"xc/app/web"
	"xc/work"
)

// Worker TODO:这里还需要做代码优化
type Worker struct {
	CurrentCh map[string]chan string
}

var worker = Worker{}

var workOnce sync.Once

// 初始化worker
func init() {
	workOnce.Do(func() {
		cch := make(map[string]chan string)
		worker.CurrentCh = cch
	})
}

// 定时处理任务
// 防止事件通知不到位的情况，这样可以主动去拉取
func (wer *Worker) doWorkTicker() {
	ticker := time.NewTicker(time.Second * time.Duration(viper.GetInt("cluster.heart_time")))

	go func() {
		for t := range ticker.C {
			log.Info("===========    定时器处理worker工作[开始]    ===========")
			log.Info("[worker]", t)
			if node.leader == true {
				wer.doLeaderWork()
			} else {
				wer.doFollowerWork()
			}
			log.Info("===========    定时器处理worker工作[结束]    ===========")
		}
	}()
}

// DoWork 开始处理任务
func (wer *Worker) DoWork() {
	log.Info("===========    监听处理worker工作[开始]    ===========")
	if node.leader == true {
		wer.doLeaderWork()
	} else {
		wer.doFollowerWork()
	}
	log.Info("===========    监听处理worker工作[结束]    ===========")
}

// leader work
func (wer *Worker) doLeaderWork() {
	log.Info("[worker][leader]开始工作")

	// 1. 动态计算nodes任务分配数量
	nodesTaskArray := wer.getNewNodesTask()

	// 2. 分发任务到zookeeper
	wer.updateNodeTaskForZookeeper(nodesTaskArray)

	// 3. 开启http
	wer.startWeb()
}

// follower work
func (wer *Worker) doFollowerWork() {
	// 等待任务分配，此时不做其他任何事情
	log.Info("[worker][follower]开始工作")
}

// getNodeTask 获取节点任务
//
// 当前算法是循环处理，逐一给每个node添加任务，保证每个node都能被轮询到任务
func (wer *Worker) getNewNodesTask() map[string]map[string]int {
	taskList := task.GetTaskList()

	if len(taskList) == 0 {
		log.Info("[worker]当前task信息为空，不进行任何处理")
		return make(map[string]map[string]int, 0)
	}

	return wer.calcNodeTaskWorkNum(taskList, nodes.children, nodes.childNum)
}

// CalcNodeTaskWorkNum 计算平均每个节点有需要处理多少个任务
// 返回结果：map[aa:map[test01:2 test02:1] bb:map[test01:1 test02:1]]
func (wer *Worker) calcNodeTaskWorkNum(taskList []TaskInfo, nodesChildren []string, nodesChildNum int) map[string]map[string]int {
	nodeTaskList := make(map[string]map[string]int)
	nodeCounter := 0

	for i := 0; i < len(nodeTaskList); i++ {
		nodeTaskList[nodesChildren[i]] = make(map[string]int)
	}

	// 计算每个节点多少个task
	for _, t := range taskList {
		for workNum := 0; workNum < t.WorkerNum; workNum++ {
			// 先判断该node上面是否存在此task，存在则+1，不存创建
			if _, ok := nodeTaskList[nodesChildren[nodeCounter]][t.Name]; ok {
				// 存在
				nodeTaskList[nodesChildren[nodeCounter]][t.Name]++
			} else {
				// 不存在
				if nodeTaskList[nodesChildren[nodeCounter]] == nil {
					tk := make(map[string]int)
					tk[t.Name] = 1
					nodeTaskList[nodesChildren[nodeCounter]] = tk
				} else {
					nodeTaskList[nodesChildren[nodeCounter]][t.Name] = 1
				}
			}

			// 循环获取nodeTask
			nodeCounter++
			if nodeCounter >= nodesChildNum {
				nodeCounter = 0
			}
		}
	}

	log.Info("[worker]work分配结果：", nodeTaskList)

	return nodeTaskList
}

func (wer *Worker) updateNodeTaskForZookeeper(nodesTaskArray map[string]map[string]int) {
	nodeTask.UpdateNodeTaskForZookeeper(nodesTaskArray)
}

// AdjustNodeTaskWork 动态计算集群中每个node应该分配多少任务数量
func (wer *Worker) AdjustNodeTaskWork() {
	taskList := task.GetFormatTaskList()
	nodeTaskList, _ := nodeTask.GetNodeTaskList()

	for nodeName, nodeTaskInfo := range nodeTaskList {
		if nodeName == node.name {
			for taskName, needWorkerNum := range nodeTaskInfo {

				var currentTaskNodeWorkNum int = 0

				// 判断当前work和需要的work数量是否相等
				for chTaskNodeName, _ := range wer.CurrentCh {
					chTaskNode := strings.Split(chTaskNodeName, "-")
					if chTaskNode[0] == taskName && chTaskNode[1] == node.name {
						currentTaskNodeWorkNum++
					}
				}

				if currentTaskNodeWorkNum < needWorkerNum {
					// 新增work
					needAddWorkNum := needWorkerNum - currentTaskNodeWorkNum

					log.Info("[worker]当前work数量：", currentTaskNodeWorkNum)
					log.Info("[worker]需要work数量：", needWorkerNum)
					log.Info("[worker][新增]work数量：", needAddWorkNum)

					for i := 0; i < needAddWorkNum; i++ {
						// 生成新的work
						chName := taskName + "-" + node.name + "-" + strconv.Itoa(rand.Int())
						message := make(chan string, 1)
						wer.CurrentCh[chName] = message
						message <- "open"

						go wer.doTaskWork(taskName, taskList[taskName], node.name, rand.Int(), message)
					}
				} else if currentTaskNodeWorkNum == needWorkerNum {
					// 不做处理
					log.Info("[worker]当前work数量：", currentTaskNodeWorkNum)
					log.Info("[worker]需要work数量：", needWorkerNum)
					log.Info("[worker][无]不需要处理")
				} else if currentTaskNodeWorkNum > needWorkerNum {
					// 关闭掉一些work
					needDelWorkNum := currentTaskNodeWorkNum - needWorkerNum

					log.Info("[worker]当前work数量：", currentTaskNodeWorkNum)
					log.Info("[worker]需要work数量：", needWorkerNum)
					log.Info("[worker][删除]work数量：", needDelWorkNum)

					for chTaskNodeName, chanMessage := range wer.CurrentCh {
						chTaskNode := strings.Split(chTaskNodeName, "-")

						if chTaskNode[0] == taskName && chTaskNode[1] == node.name {
							chanMessage <- "close"
							close(chanMessage)

							delete(wer.CurrentCh, chTaskNodeName)

							needDelWorkNum--
							if needDelWorkNum <= 0 {
								break
							}
						}
					}
				}
			}
		}
	}
	log.Info("[worker]当前节点worker运行情况：", wer.CurrentCh)
}

//doTaskWork 执行任务工作
func (wer *Worker) doTaskWork(taskName string, workName string, nodeName string, codeNum int, message chan string) {
	err := wer.doTaskWorkRun(taskName, workName, nodeName, codeNum, message)
	if err != nil {
		return
	}
}

// doTaskWorkRun 反射执行任务
// 如果方法不存在则退出
func (wer *Worker) doTaskWorkRun(taskName string, workName string, nodeName string, codeNum int, message chan string) error {
	w := new(work.Work)
	w.TaskName = taskName
	w.WorkName = workName
	w.NodeName = nodeName
	w.CodeNum = codeNum
	w.Message = message

	v := reflect.ValueOf(w)

	mv := v.MethodByName("Run" + helper.FirstLetterToUpper(workName))

	if mv == (reflect.Value{}) {
		return errors.New("[worker] " + workName + " 方法不存在")
	}

	var args []reflect.Value

	// 调用函数
	mv.Call(args)

	return nil
}

//startWeb 启动web
func (wer *Worker) startWeb() {
	enable := viper.GetBool("http.enable")
	if enable == true {
		web.New()
		go web.RunHttpServer()
	}
}
